﻿using CQRSFramework.Common.Components;
using CQRSFramework.Events;
using CQRSFramework.Events.Impl;
using System;
using System.Collections.Generic;

namespace CQRSFramework.Domains.Impl
{
    public class AggregateRoot : IAggregateRoot
    {
        public string Id { get; private set; }
        public int Version { get; private set; }

        public string UniqueAggregateRootId => Id;

        private static readonly List<IDomainEvent> _emptyEvents = new List<IDomainEvent>();
        private List<IDomainEvent> _unCommittedChanges = new List<IDomainEvent>();
        private static readonly IAggregateRootInternalEventHandlerProvider _aggregateRootInternalEventHandlerProvider = ObjectContainer.Resolve<IAggregateRootInternalEventHandlerProvider>();

        public AggregateRoot(string id)
        {
            Id = id;
        }
        public IEnumerable<IDomainEvent> GetUnCommittedDomainEvents()
        {
            if (_unCommittedChanges == null)
            {
                return _emptyEvents;
            }
            var changes = _unCommittedChanges.ToArray();
            return changes;
        }
        public void ApplyChange(IDomainEvent domainEvent)
        {
            Handle(domainEvent);
            domainEvent.AggregateRootId = Id;
            domainEvent.Version = Version + 1;
            Version++;
            if (_unCommittedChanges == null)
            {
                _unCommittedChanges = new List<IDomainEvent>();
            }
            _unCommittedChanges.Add(domainEvent);
        }
        public void AcceptDomainEvents(int version)
        {
            if (version != Version)
            {
                throw new Exception($"Can not accept invalid version:{version};expect version:{version},current version:{Version}");
            }
            _unCommittedChanges.Clear();
        }
        private void Handle(IDomainEvent domainEvent)
        {
            var handle = _aggregateRootInternalEventHandlerProvider.GetInternalEventHandler(this.GetType(), domainEvent.GetType());
            if (handle == null)
            {
                throw new Exception($"Could not find event handler for {domainEvent.GetType().FullName} of {GetType().FullName}");
            }
            if (domainEvent.Version == 1)
            {
                Id = domainEvent.AggregateRootId;
            }
            handle(this, domainEvent);
        }

        public void LoadFromHistory(IEnumerable<DomainEventStream> history)
        {
            foreach (var item in history)
            {
                foreach (var evnt in item.DomainEvents)
                {
                    Handle(evnt);
                    Version++;
                }
            }
        }


    }
}
